#ifndef __VV_VERTEX_HPP__
#define __VV_VERTEX_HPP__

#include <list>
#include <queue>
#include <memory>
#include <util/undeletable_ptr.hpp>

namespace vv {
  class  abstract_vertex;
  class  vptr;

  /** @brief A list type for assigning neighbourhoods to vertices. */
  typedef std::list<vptr> nb;

  /** @brief The pointer that should be used for external accesses to
             vertices.

      This class will handle proper switching to and from the
      synchronised state of a vertex through the member inderection
      method.
  */
  class vptr {
  public:
    vptr();
    vptr(abstract_vertex* v);
    vptr(const vv::vptr& v);
    virtual ~vptr();

    vptr& operator=(const vv::vptr& v);

    abstract_vertex* operator->() const;
    abstract_vertex& operator*() const;
    abstract_vertex* raw() const;

    operator bool() const;

  private:
    void release();
    abstract_vertex* ptr;
  };

  /** @brief A base class for a vertex iterator */
  class abstract_vertex_iterator {
  public:
    virtual ~abstract_vertex_iterator();
    virtual vptr first() = 0;
    virtual vptr advance() = 0;
    virtual bool not_finished() = 0;
  };

  /** @brief A typedef for simplifying vertex iterator allocation with an auto_ptr */
  typedef std::auto_ptr<vv::abstract_vertex_iterator> iptr;

  /** @brief An iterator for the vertex neighbourhoods */
  class neighbourhood_iterator : public abstract_vertex_iterator {
  public:
    neighbourhood_iterator(vptr v, bool old);
    vptr first();
    vptr advance();
    bool not_finished();
  private:
    vptr v;
    vptr start;
    vptr current;
    bool old;
    bool returned_first;
  };

  /** @brief The abstract_vertex class.
   */
  class abstract_vertex {
    friend class vertex_factory;

  private:
    bool gc_mark;

  protected:
    class abstract_edge;
    class abstract_properties;

  public:
    abstract_vertex();
    virtual ~abstract_vertex();

    // Functions for reference counting
    void refp();
    void refm();
    unsigned int references() const;

    // synchronisation functions
    virtual vptr old();
    virtual void synchronise();
    void         release_old();

    // flag commands
    void flag(vptr v);
    vptr flag();

    // return a neighbourhood iterator
    neighbourhood_iterator* iter();

    // neighbourhood query operations
    unsigned int valence();
    bool         in(vptr v);
    vptr         any();
    vptr         next(vptr v, unsigned int index = 1);
    vptr         prev(vptr v, unsigned int index = 1);

    // queries using the flag
    vptr         next_flag(unsigned int index = 1);
    vptr         prev_flag(unsigned int index = 1);

    // neighbourhood editing operations
    void assign_nb(const vv::nb& n);
    void remove(vptr v);
    void replace(vptr t, vptr v);
    void splice_before(vptr t, vptr v);
    void splice_after(vptr t, vptr v);

    // editing using the flag
    void remove_flag();
    void replace_flag(vptr v);
    void splice_before_flag(vptr v);
    void splice_after_flag(vptr v);

    // property functions
    abstract_properties* properties();

    // edge functions
    abstract_edge* asymmetric_edge(vptr v);
    abstract_edge* symmetric_edge(vptr v);

  protected:
    class abstract_edge {
    public:
      virtual ~abstract_edge() {}
    };
    class abstract_properties {
    public:
      virtual ~abstract_properties() {}
    };

    virtual abstract_edge* new_edge() = 0;
    virtual void copy_edge(abstract_edge* from, abstract_edge* to) = 0;

    virtual abstract_properties* new_properties() = 0;
    virtual void copy_properties(abstract_properties* from, abstract_properties* to) = 0;

    void init_props();

  private:
    typedef util::undeletable_ptr<abstract_vertex> nptr;
    typedef std::pair<nptr, abstract_edge*>        nb_pair;
    typedef std::list<nb_pair>                     neighbourhood;
    typedef std::list<nb_pair>::iterator           niter;
    typedef std::queue<nptr>                       vq;

    class nb_find_predicate {
    public:
      nb_find_predicate(abstract_vertex* v);
      bool operator()(const nb_pair& np);
    private:
      abstract_vertex* v;
    };

    virtual void reset_state();
    void         synch_edge(nptr v);
    void         synch_edges();

    unsigned int refs;
    unsigned int old_state;

    neighbourhood  nb_current;
    neighbourhood  nb_old;
    neighbourhood* nb;

    abstract_properties* props_current;
    abstract_properties* props_old;    
    abstract_properties* props;

    vq unsynched_edges;

    niter  flag_current;
    niter  flag_old;
    niter* fl;
  };
}

bool operator< (const vv::vptr& a, const vv::vptr& b);
bool operator> (const vv::vptr& a, const vv::vptr& b);
bool operator==(const vv::vptr& a, const vv::vptr& b);
bool operator!=(const vv::vptr& a, const vv::vptr& b);

#endif
